package xyz.monkeytong.hongbao.services;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcelable;
import android.graphics.Path;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.util.DisplayMetrics;

import xyz.monkeytong.hongbao.utils.HongbaoSignature;
import xyz.monkeytong.hongbao.utils.PowerUtil;

import java.util.List;

public class HongbaoService extends AccessibilityService implements SharedPreferences.OnSharedPreferenceChangeListener {
  private static final String WECHAT_DETAILS_EN = "Details";
  private static final String WECHAT_DETAILS_CH = "红包详情";
  private static final String WECHAT_BETTER_LUCK_EN = "Better luck next time!";
  private static final String WECHAT_BETTER_LUCK_CH = "手慢了";
  private static final String WECHAT_EXPIRES_CH = "已超过24小时";
  private static final String WECHAT_VIEW_SELF_CH = "查看红包";
  private static final String WECHAT_VIEW_OTHERS_CH = "领取红包";
  private static final String WECHAT_NOTIFICATION_TIP = "[微信红包]";
  private static final String WECHAT_LUCKMONEY_RECEIVE_ACTIVITY = "LuckyMoneyReceiveUI";
  private static final String WECHAT_LUCKMONEY_DETAIL_ACTIVITY = "LuckyMoneyDetailUI";
  private static final String WECHAT_LUCKMONEY_GENERAL_ACTIVITY = "LauncherUI";
  private static final String WECHAT_LUCKMONEY_CHATTING_ACTIVITY = "ChattingUI";
  private String currentActivityName = WECHAT_LUCKMONEY_GENERAL_ACTIVITY;

  private AccessibilityNodeInfo rootNodeInfo, mReceiveNode, mUnpackNode;
  private boolean mLuckyMoneyPicked, mLuckyMoneyReceived;
  private int mUnpackCount = 0;
  private boolean mMutex = false, mListMutex = false, mChatMutex = false;
  private HongbaoSignature signature = new HongbaoSignature();

  private PowerUtil powerUtil;
  private SharedPreferences sharedPreferences;

  /**
   * AccessibilityEvent
   *
   * @param event 事件
   */
  @Override
  public void onAccessibilityEvent(AccessibilityEvent event) {
    if (sharedPreferences == null) return;

    setCurrentActivityName(event);

    //  检测通知消息
    if (!mMutex) {
      if (sharedPreferences.getBoolean("pref_watch_notification", false) && watchNotifications(event))
        return;
      if (sharedPreferences.getBoolean("pref_watch_list", false) && watchList(event)) return;
      mListMutex = false;
    }

    if (!mChatMutex) {
      mChatMutex = true;
      if (sharedPreferences.getBoolean("pref_watch_chat", false)) watchChat(event);
      mChatMutex = false;
    }
  }

  private void watchChat(AccessibilityEvent event) {
    this.rootNodeInfo = getRootInActiveWindow();

    if (rootNodeInfo == null) return;

    mReceiveNode = null;
    mUnpackNode = null;

    checkNodeInfo(event.getEventType());

        /* 如果已经接收到红包并且还没有戳开 */
    if (mLuckyMoneyReceived && !mLuckyMoneyPicked && (mReceiveNode != null)) {
      mMutex = true;

      mReceiveNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
      mLuckyMoneyReceived = false;
      mLuckyMoneyPicked = true;
    }
        /* 如果戳开但还未领取 */
    if (mUnpackCount == 1 && (mUnpackNode != null)) {
      int delayFlag = sharedPreferences.getInt("pref_open_delay", 0) * 1000;
      new android.os.Handler().postDelayed(
          new Runnable() {
            public void run() {
              try {
                openPacket();
              } catch (Exception e) {
                mMutex = false;
                mLuckyMoneyPicked = false;
                mUnpackCount = 0;
              }
            }
          },
          delayFlag);
    }
  }

  private void openPacket() {
    DisplayMetrics metrics = getResources().getDisplayMetrics();
    float dpi = metrics.density;
    if (android.os.Build.VERSION.SDK_INT <= 23) {
      mUnpackNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
    } else {
      if (android.os.Build.VERSION.SDK_INT > 23) {

        Path path = new Path();
        if (640 == dpi) {
          path.moveTo(720, 1575);
        } else {
          path.moveTo(540, 1060);
        }
        GestureDescription.Builder builder = new GestureDescription.Builder();
        GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 450, 50)).build();
        dispatchGesture(gestureDescription, new GestureResultCallback() {
          @Override
          public void onCompleted(GestureDescription gestureDescription) {
            Log.d("test", "onCompleted");
            mMutex = false;
            super.onCompleted(gestureDescription);
          }

          @Override
          public void onCancelled(GestureDescription gestureDescription) {
            Log.d("test", "onCancelled");
            mMutex = false;
            super.onCancelled(gestureDescription);
          }
        }, null);

      }
    }
  }

  private void setCurrentActivityName(AccessibilityEvent event) {
    if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
      return;
    }

    try {
      ComponentName componentName = new ComponentName(
          event.getPackageName().toString(),
          event.getClassName().toString()
      );

      getPackageManager().getActivityInfo(componentName, 0);
      currentActivityName = componentName.flattenToShortString();
    } catch (PackageManager.NameNotFoundException e) {
      currentActivityName = WECHAT_LUCKMONEY_GENERAL_ACTIVITY;
    }
  }

  private boolean watchList(AccessibilityEvent event) {
    if (mListMutex) return false;
    mListMutex = true;
    AccessibilityNodeInfo eventSource = event.getSource();
    // Not a message
    if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || eventSource == null)
      return false;

    List<AccessibilityNodeInfo> nodes = eventSource.findAccessibilityNodeInfosByText(WECHAT_NOTIFICATION_TIP);
    //增加条件判断currentActivityName.contains(WECHAT_LUCKMONEY_GENERAL_ACTIVITY)
    //避免当订阅号中出现标题为“[微信红包]拜年红包”（其实并非红包）的信息时误判
    if (!nodes.isEmpty() && currentActivityName.contains(WECHAT_LUCKMONEY_GENERAL_ACTIVITY)) {
      AccessibilityNodeInfo nodeToClick = nodes.get(0);
      if (nodeToClick == null) return false;
      CharSequence contentDescription = nodeToClick.getContentDescription();
      if (contentDescription != null && !signature.getContentDescription().equals(contentDescription)) {
        nodeToClick.performAction(AccessibilityNodeInfo.ACTION_CLICK);
        signature.setContentDescription(contentDescription.toString());
        return true;
      }
    }
    return false;
  }

  private boolean watchNotifications(AccessibilityEvent event) {
    // Not a notification
    if (event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)
      return false;

    // Not a hongbao
    String tip = event.getText().toString();
    if (!tip.contains(WECHAT_NOTIFICATION_TIP)) return true;

    Parcelable parcelable = event.getParcelableData();
    if (parcelable instanceof Notification) {
      Notification notification = (Notification) parcelable;
      try {
                /* 清除signature,避免进入会话后误判 */
        signature.cleanSignature();

        notification.contentIntent.send();
      } catch (PendingIntent.CanceledException e) {
        e.printStackTrace();
      }
    }
    return true;
  }

  @Override
  public void onInterrupt() {

  }

  private AccessibilityNodeInfo findOpenButton(AccessibilityNodeInfo node) {
    if (node == null)
      return null;

    //非layout元素
    if (node.getChildCount() == 0) {
      // 微信6.5.6 可点击的变为LinearLayout
      if (("android.widget.Button".equals(node.getClassName())) || ("android.widget.LinearLayout".equals(node.getClassName()) && node.isClickable()))
        return node;
      else
        return null;
    }

    //layout元素，遍历找button
    AccessibilityNodeInfo button;
    for (int i = 0; i < node.getChildCount(); i++) {
      button = findOpenButton(node.getChild(i));
      if (button != null)
        return button;
    }
    return null;
  }

  private void checkNodeInfo(int eventType) {
    if (this.rootNodeInfo == null) return;

    if (signature.commentString != null) {
      sendComment();
      signature.commentString = null;
    }

        /* 聊天会话窗口，遍历节点匹配“领取红包”和"查看红包" */
    AccessibilityNodeInfo node1 = (sharedPreferences.getBoolean("pref_watch_self", false)) ?
        this.getTheLastNode(WECHAT_VIEW_OTHERS_CH, WECHAT_VIEW_SELF_CH) : this.getTheLastNode(WECHAT_VIEW_OTHERS_CH);
    if (node1 != null &&
        (currentActivityName.contains(WECHAT_LUCKMONEY_CHATTING_ACTIVITY)
            || currentActivityName.contains(WECHAT_LUCKMONEY_GENERAL_ACTIVITY))) {
      String excludeWords = sharedPreferences.getString("pref_watch_exclude_words", "");
      if (this.signature.generateSignature(node1, excludeWords)) {
        mLuckyMoneyReceived = true;
        mReceiveNode = node1;
        Log.d("sig", this.signature.toString());
      }
      return;
    }

        /* 戳开红包，红包还没抢完，遍历节点匹配“拆红包” */
    AccessibilityNodeInfo node2 = findOpenButton(this.rootNodeInfo);
    if (node2 != null && "android.widget.Button".equals(node2.getClassName()) && currentActivityName.contains(WECHAT_LUCKMONEY_RECEIVE_ACTIVITY)) {
      mUnpackNode = node2;
      mUnpackCount += 1;
      return;
    }

        /* 戳开红包，红包已被抢完，遍历节点匹配“红包详情”和“手慢了” */
    boolean hasNodes = this.hasOneOfThoseNodes(
        WECHAT_BETTER_LUCK_CH, WECHAT_DETAILS_CH,
        WECHAT_BETTER_LUCK_EN, WECHAT_DETAILS_EN, WECHAT_EXPIRES_CH);
    if (mMutex && eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && hasNodes
        && (currentActivityName.contains(WECHAT_LUCKMONEY_DETAIL_ACTIVITY)
        || currentActivityName.contains(WECHAT_LUCKMONEY_RECEIVE_ACTIVITY))) {
      mMutex = false;
      mLuckyMoneyPicked = false;
      mUnpackCount = 0;
      performGlobalAction(GLOBAL_ACTION_BACK);
      signature.commentString = generateCommentString();
    }
  }

  private void sendComment() {
    try {
      AccessibilityNodeInfo outNode =
          getRootInActiveWindow().getChild(0).getChild(0);
      AccessibilityNodeInfo nodeToInput = outNode.getChild(outNode.getChildCount() - 1).getChild(0).getChild(1);

      if ("android.widget.EditText".equals(nodeToInput.getClassName())) {
        Bundle arguments = new Bundle();
        arguments.putCharSequence(AccessibilityNodeInfo
            .ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, signature.commentString);
        nodeToInput.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
      }
    } catch (Exception e) {
      // Not supported
    }
  }


  private boolean hasOneOfThoseNodes(String... texts) {
    List<AccessibilityNodeInfo> nodes;
    for (String text : texts) {
      if (text == null) continue;

      nodes = this.rootNodeInfo.findAccessibilityNodeInfosByText(text);

      if (nodes != null && !nodes.isEmpty()) return true;
    }
    return false;
  }

  private AccessibilityNodeInfo getTheLastNode(String... texts) {
    int bottom = 0;
    AccessibilityNodeInfo lastNode = null, tempNode;
    List<AccessibilityNodeInfo> nodes;

    for (String text : texts) {
      if (text == null) continue;

      nodes = this.rootNodeInfo.findAccessibilityNodeInfosByText(text);

      if (nodes != null && !nodes.isEmpty()) {
        tempNode = nodes.get(nodes.size() - 1);
        if (tempNode == null) return null;
        Rect bounds = new Rect();
        tempNode.getBoundsInScreen(bounds);
        if (bounds.bottom > bottom) {
          bottom = bounds.bottom;
          lastNode = tempNode;
          signature.others = text.equals(WECHAT_VIEW_OTHERS_CH);
        }
      }
    }
    return lastNode;
  }

  @Override
  public void onServiceConnected() {
    super.onServiceConnected();
    this.watchFlagsFromPreference();
  }

  private void watchFlagsFromPreference() {
    sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
    sharedPreferences.registerOnSharedPreferenceChangeListener(this);

    this.powerUtil = new PowerUtil(this);
    Boolean watchOnLockFlag = sharedPreferences.getBoolean("pref_watch_on_lock", false);
    this.powerUtil.handleWakeLock(watchOnLockFlag);
  }

  @Override
  public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    if (key.equals("pref_watch_on_lock")) {
      Boolean changedValue = sharedPreferences.getBoolean(key, false);
      this.powerUtil.handleWakeLock(changedValue);
    }
  }

  @Override
  public void onDestroy() {
    this.powerUtil.handleWakeLock(false);
    super.onDestroy();
  }

  private String generateCommentString() {
    if (!signature.others) return null;

    Boolean needComment = sharedPreferences.getBoolean("pref_comment_switch", false);
    if (!needComment) return null;

    String[] wordsArray = sharedPreferences.getString("pref_comment_words", "").split(" +");
    if (wordsArray.length == 0) return null;

    Boolean atSender = sharedPreferences.getBoolean("pref_comment_at", false);
    if (atSender) {
      return "@" + signature.sender + " " + wordsArray[(int) (Math.random() * wordsArray.length)];
    } else {
      return wordsArray[(int) (Math.random() * wordsArray.length)];
    }
  }
}
